package com.baofu.trade.indicator;

import com.baofu.trade.model.indicator.BollingerBands;
import com.baofu.trade.model.indicator.FibonacciLevels;
import com.baofu.trade.model.indicator.MACDResult;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Component
public class TechnicalIndicator {
    
    /**
     * 计算EMA
     */
    public BigDecimal calculateEMA(List<BigDecimal> prices, int period) {
        if (prices.size() < period) return BigDecimal.ZERO;
        Collections.reverse(prices);
        // 创建正序的价格列表（从旧到新）
        List<BigDecimal> orderedPrices = new ArrayList<>(prices);

        // 计算第一个SMA作为EMA的初始值
        BigDecimal sum = orderedPrices.stream()
            .limit(period)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal ema = sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
        
        // 计算乘数
        BigDecimal multiplier = new BigDecimal(2.0 / (period + 1.0))
            .setScale(8, RoundingMode.HALF_UP);
        BigDecimal oneMinusMultiplier = BigDecimal.ONE.subtract(multiplier);
        
        // 从第period+1个数据开始计算EMA
        for (int i = period; i < orderedPrices.size(); i++) {
            ema = orderedPrices.get(i).multiply(multiplier)
                .add(ema.multiply(oneMinusMultiplier))
                .setScale(8, RoundingMode.HALF_UP);
        }
        
        return ema.setScale(2, RoundingMode.HALF_UP);
    }
    
    /**
     * 计算布林带
     */
    public BollingerBands calculateBollingerBands(List<BigDecimal> prices, int period, int multiplier) {
        BollingerBands bands = new BollingerBands();
        // 1. 计算中轨（SMA）
        BigDecimal sum = BigDecimal.ZERO;
        for (BigDecimal price : prices.subList(0, period)) {
            sum = sum.add(price);
        }
        BigDecimal sma = sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
        
        // 2. 计算标准差
        BigDecimal sumSquaredDeviations = BigDecimal.ZERO;
        for (int i = 0; i < period; i++) {
            BigDecimal price = prices.get(i);
            BigDecimal deviation = price.subtract(sma);
            BigDecimal squaredDeviation = deviation.multiply(deviation);
            sumSquaredDeviations = sumSquaredDeviations.add(squaredDeviation);
        }
        
        BigDecimal variance = sumSquaredDeviations.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
        BigDecimal standardDeviation = BigDecimal.valueOf(Math.sqrt(variance.doubleValue()));
        
        // 3. 计算布林带
        bands.setMiddle(sma.setScale(2, RoundingMode.HALF_UP));
        bands.setUpper(sma.add(standardDeviation.multiply(new BigDecimal(multiplier)))
            .setScale(2, RoundingMode.HALF_UP));
        bands.setLower(sma.subtract(standardDeviation.multiply(new BigDecimal(multiplier)))
            .setScale(2, RoundingMode.HALF_UP));
        
        return bands;
    }
    
    /**
     * 计算斐波那契回调位
     */
    public FibonacciLevels calculateFibonacciLevels(BigDecimal highPrice, BigDecimal lowPrice) {
        FibonacciLevels levels = new FibonacciLevels();
        BigDecimal range = highPrice.subtract(lowPrice);
        
        levels.setLevel0(lowPrice.setScale(2, RoundingMode.HALF_UP));
        levels.setLevel236(lowPrice.add(range.multiply(new BigDecimal("0.236")))
            .setScale(2, RoundingMode.HALF_UP));
        levels.setLevel382(lowPrice.add(range.multiply(new BigDecimal("0.382")))
            .setScale(2, RoundingMode.HALF_UP));
        levels.setLevel500(lowPrice.add(range.multiply(new BigDecimal("0.500")))
            .setScale(2, RoundingMode.HALF_UP));
        levels.setLevel618(lowPrice.add(range.multiply(new BigDecimal("0.618")))
            .setScale(2, RoundingMode.HALF_UP));
        levels.setLevel786(lowPrice.add(range.multiply(new BigDecimal("0.786")))
            .setScale(2, RoundingMode.HALF_UP));
        levels.setLevel1000(highPrice.setScale(2, RoundingMode.HALF_UP));
        
        return levels;
    }
    
    /**
     * 计算MACD指标 - 币安算法
     * 1. 首日EMA = 首日收盘价
     * 2. 其他日EMA = 前一日EMA × (N-1)/(N+1) + 今日收盘价 × 2/(N+1)
     * 3. DIF = EMA(12) - EMA(26)
     * 4. DEA = DIF的9日EMA
     * 5. MACD = (DIF-DEA) × 2
     */
    public MACDResult calculateMACD(List<BigDecimal> prices) {
        // 确保价格列表足够长
        if (prices.size() < 26) {
            throw new IllegalArgumentException("需要至少26个价格数据点来计算MACD");
        }
        
        // 反转列表，使其按时间从旧到新排序
        List<BigDecimal> orderedPrices = new ArrayList<>(prices);
        Collections.reverse(orderedPrices);
        
        // 币安EMA计算系数
        BigDecimal k12 = new BigDecimal("2").divide(new BigDecimal("13"), 10, RoundingMode.HALF_UP);
        BigDecimal k26 = new BigDecimal("2").divide(new BigDecimal("27"), 10, RoundingMode.HALF_UP);
        BigDecimal k9 = new BigDecimal("2").divide(new BigDecimal("10"), 10, RoundingMode.HALF_UP);
        
        // 存储每个时间点的值
        List<BigDecimal> ema12List = new ArrayList<>();
        List<BigDecimal> ema26List = new ArrayList<>();
        List<BigDecimal> difList = new ArrayList<>();
        List<BigDecimal> deaList = new ArrayList<>();
        List<BigDecimal> macdList = new ArrayList<>();
        
        // 初始化第一个值
        ema12List.add(orderedPrices.get(0));
        ema26List.add(orderedPrices.get(0));
        
        // 计算每个时间点的EMA
        for (int i = 1; i < orderedPrices.size(); i++) {
            BigDecimal price = orderedPrices.get(i);
            
            // 计算EMA12
            BigDecimal ema12 = price.multiply(k12)
                .add(ema12List.get(i-1).multiply(BigDecimal.ONE.subtract(k12)));
            ema12List.add(ema12);
            
            // 计算EMA26
            BigDecimal ema26 = price.multiply(k26)
                .add(ema26List.get(i-1).multiply(BigDecimal.ONE.subtract(k26)));
            ema26List.add(ema26);
            
            // 计算DIF
            BigDecimal dif = ema12.subtract(ema26);
            difList.add(dif);
            
            // 计算DEA (从第一个DIF值开始)
            BigDecimal dea;
            if (difList.size() == 1) {
                dea = dif;
            } else {
                dea = dif.multiply(k9).add(deaList.get(deaList.size()-1).multiply(BigDecimal.ONE.subtract(k9)));
            }
            deaList.add(dea);
            
            // 计算MACD
            BigDecimal macd = dif.subtract(dea).multiply(new BigDecimal("2"));
            macdList.add(macd);
        }
        
        // 获取最新值
        BigDecimal finalDif = difList.get(difList.size()-1);
        BigDecimal finalDea = deaList.get(deaList.size()-1);
        BigDecimal finalMacd = macdList.get(macdList.size()-1);
        
        // 创建结果对象
        MACDResult result = new MACDResult();
        result.setDif(finalDif.setScale(2, RoundingMode.HALF_UP));
        result.setDea(finalDea.setScale(2, RoundingMode.HALF_UP));
        result.setBar(finalDif.subtract(finalDea).setScale(2, RoundingMode.HALF_UP));
        result.setMacd(finalMacd.setScale(2, RoundingMode.HALF_UP));
        
        // 计算 BAR 值
        BigDecimal bar = finalDif.subtract(finalDea);
        
        // 趋势判断和分析
        StringBuilder analysis = new StringBuilder();
        
        // 判断MACD柱的颜色和基本趋势 (根据BAR值判断，而不是MACD值)
        if (bar.compareTo(BigDecimal.ZERO) > 0) {
            result.setHistogram("绿柱");  // BAR > 0 表示上涨，显示绿柱
            result.setTrend("上涨趋势");
            analysis.append("DIF大于DEA，当前处于上涨趋势。");
        } else {
            result.setHistogram("红柱");  // BAR < 0 表示下跌，显示红柱
            result.setTrend("下跌趋势");
            analysis.append("DIF小于DEA，当前处于下跌趋势。");
        }
        
        // 判断DIF和DEA的关系（金叉死叉）
        if (finalDif.compareTo(finalDea) > 0) {
            analysis.append("DIF线位于DEA线上方，");
            if (difList.get(difList.size()-2).compareTo(deaList.get(deaList.size()-2)) <= 0) {
                analysis.append("形成黄金交叉，可能出现上涨行情，考虑买入。");
            } else {
                analysis.append("维持上升趋势。");
            }
        } else {
            analysis.append("DIF线位于DEA线下方，");
            if (difList.get(difList.size()-2).compareTo(deaList.get(deaList.size()-2)) >= 0) {
                analysis.append("形成死亡交叉，可能出现下跌行情，考虑卖出。");
            } else {
                analysis.append("维持下降趋势。");
            }
        }
        
        // 判断MACD柱的变化趋势
        if (Math.abs(finalMacd.doubleValue()) < Math.abs(macdList.get(macdList.size()-2).doubleValue())) {
            analysis.append("MACD柱体缩短，趋势可能减弱。");
        } else {
            analysis.append("MACD柱体增长，趋势可能增强。");
        }
        
        result.setAnalysis(analysis.toString());
        
        return result;
    }
} 